package sys.fsm;

import java.util.*;
import java.io.*;

import sys.database.*;
import sys.transport.*;
import sys.message.*;
import org.json.*;
import sys.utils.*;

public class Automat
{
    public Automat(int debugLevel, String confFilePath)
    {
        _fsm = new AutomatContext(this);
        timer = new Timer();
        readParams(confFilePath);

        ms = new MessageSenderSMTP(mailserver, "10025");

        db = new DBWrapper(DBServer, DB, DBUser, DBPass);
        db.connect();
        
        this.debugLevel = debugLevel;

        n1 = 0; n2 = 0; n3 = 0; n4 = 0;

        this.ackBySeller = 0;
        this.ackByUser = 0;
        this.payed = 0;

        // Uncomment to see debug output.
        _fsm.setDebugFlag(true);
    }

    private void readParams(String confFilePath)
    {
        File confFile = new File(confFilePath);
        String conf = ReadWriteTextFile.getContents(confFile);

        try
        {
            JSONObject jo = new JSONObject(conf);
            n1_max = jo.getInt("n1_max");
            timeout_1_delay = jo.getInt("timeout_1_delay");

            n2_max = jo.getInt("n2_max");
            timeout_2_delay = jo.getInt("timeout_2_delay");

            n3_max = jo.getInt("n3_max");
            timeout_3_delay = jo.getInt("timeout_3_delay");

            n4_max = jo.getInt("n4_max");
            timeout_4_delay = jo.getInt("timeout_4_delay");

            mailserver = jo.getString("mailserver");
            sysaMailAddress = jo.getString("sysaMailAddress");
            sysbMailAddress = jo.getString("sysbMailAddress");
            sysaMailPass = jo.getString("sysaMailPass");

            DBServer = jo.getString("DBServer");
            DB = jo.getString("DB");
            DBUser = jo.getString("DBUser");
            DBPass = jo.getString("DBPass");
        }
        catch(JSONException e)
        {
            System.out.println("Error while parsing config file : " + e.getMessage());
        }
    }

    /*
     * Messages 
     */

    //public void start_Message(long Id, String PSAddress, String userName, int Quantity)
    public void start_Message(long Id, String PSAddress, String userName, int quantity)
    {
        _fsm.Start(Id, PSAddress, userName, quantity);
    }

    public void ack_Seller_1_Message()
    {
        _fsm.Ack_Seller_1_T();
    }

    public void nack_Seller_1_Message()
    {
        _fsm.Nack_Seller_1_T();
    } 

    public void ack_PayingSystem_1_Message()
    {
        _fsm.Ack_PayingSystem_1_T();
    }

    public void ack_User_1_Message()
    {
        _fsm.Ack_User_1_T();
    }

    public void nack_User_1_Message()
    {
        _fsm.Nack_User_1_T();
    }

    public void ack_Seller_2_Message()
    {
        _fsm.Ack_Seller_2_T();
    }

    /*
     * Entry actions
     */

    public void wait_Seller_1_Entry_A()
    {
        //timer.schedule(new Timeout_1(), timeout_1_delay); 
        timer.schedule(new Timeout_1(), 0); 
    }

    public void wait_PayingSystem_1_Entry_A()
    {
        //timer.schedule(new Timeout_2(), timeout_2_delay); 
        timer.schedule(new Timeout_2(), 0); 
    }

    public void wait_User_1_Entry_A()
    {
        //timer.schedule(new Timeout_3(), timeout_3_delay); 
        timer.schedule(new Timeout_3(), 0); 
    }

    public void wait_Seller_2_Entry_A()
    {
        //timer.schedule(new Timeout_4(), timeout_4_delay); 
        timer.schedule(new Timeout_4(), 0); 
    }

    /*
     *  Actions
     */
    public void start_A(long Id, String PSAddress, String userName, int quantity)
    {
        System.out.println("New request. Starting timer.");

        if (debugLevel == 0)
        {
            this.Id = Id;
            this.PSAddress = PSAddress;
            this.userName = userName;
            this.quantity = quantity;
            db.insert("Request", "ID, PayingSystem_Address, User_Name, Quantity", Id + ",'" + PSAddress + "','" +  userName + "'," + quantity);
        }
    }

    /*
     * Seller've granted ack
     */
    public void ack_Seller_1_A()
    {
        System.out.println("Acknowledged by seller");

        if (debugLevel == 0)
        {
            this.ackBySeller = 1;
            db.update("Request", "ackBySeller", "1", "Id = " + Id);
        }
    }

    /*
     * Requesting ack from seller
     */
    public void timeout_1_A()
    {
        System.out.println("In timeout1. N = " + n1);

        if (debugLevel == 0)
        {
            try
            {
                JSONMessage mes = new JSONMessage(sysaMailAddress, sysbMailAddress, "Ack request");
                mes.put("Type", "newRequest");
                mes.put("Id", Id);
                mes.put("Quantity", quantity);
                mes.put("PayingSystem", PSAddress);
                ms.send(mes);
            }
            catch(Exception e)
            {
                System.out.println("Error while making JSONMessage or sending message in temeout_1_A()");
            }
        }

        n1++;
        timer.schedule(new Timeout_1(), timeout_1_delay); 
    }

    public void ack_PayingSystem_1_A()
    {
        System.out.println("Acknowledged by paying system");
        
        if (debugLevel == 0)
        {
            this.payed = 1;
            db.update("Request", "Payed", "1", "Id = " + Id);
        }
    }

    /*
     * Request ack from paying system
     */
    public void timeout_2_A()
    {
        System.out.println("In timeout2. N = " + n2);

        if (debugLevel == 0)
        {
            try
            {
                JSONMessage mes = new JSONMessage(sysaMailAddress, PSAddress, "Is request payed?");
                mes.put("ID", Id);
                mes.put("Type", "payingStatus");
                mes.put("AskAboutID", Id);
                ms.send(mes);
            }
            catch(Exception e)
            {
                System.out.println("Error while making JSONMessage or sending message in temeout_1_A()");
            }
        }

        n2++;
        timer.schedule(new Timeout_2(), timeout_2_delay); 
    }
    
    public void ack_User_1_A()
    {
        System.out.println("Acknowledged by user");

        if (debugLevel == 0)
        {
            this.ackByUser = 1;
            db.update("Request", "AckByUser", "1", "Id = " + Id);
        }
    }

    /*
     * Waiting ack from user
     */
    public void timeout_3_A()
    {
        System.out.println("In timeout3. N = " + n3);
        n3++;
        timer.schedule(new Timeout_3(), timeout_3_delay); 
    }

    public void ack_Seller_2_A()
    {
        System.out.println("Acknowledged by seller");
    }

    /*
     * Waiting second ack from seller
     */
    public void timeout_4_A()
    {
        System.out.println("In timeout4. N = " + n4);

        if (debugLevel == 0)
        {
            try
            {
                JSONMessage mes = new JSONMessage(sysaMailAddress, sysbMailAddress, "Ack request");
                mes.put("Type", "ackRequest");
                mes.put("ID", Id);
                ms.send(mes);
            }
            catch(Exception e)
            {
                System.out.println("Error while making JSONMessage or sending message in temeout_1_A()");
            }
        }

        n4++;
        timer.schedule(new Timeout_4(), timeout_4_delay); 
    }

    public void done_Entry_A()
    {
        System.out.println("Before done state");
        timer.cancel();
    }

    public void cancel_Entry_A()
    {
        System.out.println("Before cancel state");
        timer.cancel();
    }

    /*
     * Guard conditions
     */

    public boolean checkTimeout_1_Times()
    {
        if (n1 < n1_max) 
            return true;
        else
            return false;
    } 

    public boolean checkTimeout_2_Times()
    {
        if (n2 < n2_max) 
            return true;
        else
            return false;
    }   
  
    public boolean checkTimeout_3_Times()
    {
        if (n3 < n3_max) 
            return true;
        else
            return false;
    }

    public boolean checkTimeout_4_Times()
    {
        if (n4 < n4_max) 
            return true;
        else
            return false;
    }

    /*
     * Timer event handlers
     */ 
        
    class Timeout_1 extends TimerTask
    {
        public void run()
        {
            try
            {
                _fsm.Timeout_1_T();
            }
            catch(Exception e)
            {
                System.out.println("Timeout1 tryed to call illegal transition. Do nothing.");
            }
        }
    }

    class Timeout_2 extends TimerTask
    {
        public void run()
        {
            try
            {
                _fsm.Timeout_2_T();
            }
            catch(Exception e)
            {
                System.out.println("Timeout2 tryed to call illegal transition. Do nothing.");
            }
        }
    }

    class Timeout_3 extends TimerTask
    {
        public void run()
        {
            try
            {
                _fsm.Timeout_3_T();
            }
            catch(Exception e)
            {
                System.out.println("Timeout3 tryed to call illegal transition. Do nothing.");
            }
        }
    }
            
    class Timeout_4 extends TimerTask
    {
        public void run()
        {
            try
            {
                _fsm.Timeout_4_T();
            }
            catch(Exception e)
            {
                System.out.println("Timeout4 tryed to call illegal transition. Do nothing.");
            }
        }
    }

    // Request parametrs
    private String PSAddress, userName;
    private long Id;
    private int quantity;
    int payed, ackByUser, ackBySeller;

    // Configs
    String mailserver, sysaMailAddress, sysbMailAddress, sysaMailPass;
    String DBServer, DB, DBUser, DBPass;
    private int n1, n1_max, n2, n2_max, n3, n3_max, n4, n4_max;
    private long timeout_1_delay, timeout_2_delay, timeout_3_delay, timeout_4_delay;
    int debugLevel;

    private AutomatContext _fsm;
    private Timer timer;
    DBWrapper db;
    MessageSender ms;
}
